package com.example.pdftomarkdown.service;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * PDF转图片服务类
 * 负责将PDF文件的每一页转换为独立的图片
 */
@Service
public class PdfToImageConverterService {
    
    // 默认DPI设置，影响生成图片的质量
    private static final float DEFAULT_DPI = 300f;
    
    // 图片格式
    private static final String IMAGE_FORMAT = "PNG";
    
    /**
     * 将PDF文件转换为图片ZIP包
     * 每一页生成一个图片文件
     * 
     * @param file 上传的PDF文件
     * @return ZIP文件的字节数组，包含所有页面的图片
     * @throws IOException 文件处理异常
     */
    public byte[] convertPdfToImages(MultipartFile file) throws IOException {
        if (file.isEmpty()) {
            throw new IllegalArgumentException("上传的文件不能为空");
        }
        
        if (!isPdfFile(file)) {
            throw new IllegalArgumentException("只支持PDF格式文件");
        }
        
        try (InputStream inputStream = file.getInputStream();
             PDDocument document = PDDocument.load(inputStream)) {
            
            return convertDocumentToImageZip(document, file.getOriginalFilename());
        }
    }
    
    /**
     * 获取PDF文件的页数信息
     * 
     * @param file 上传的PDF文件
     * @return PDF页数信息
     * @throws IOException 文件处理异常
     */
    public PdfImageInfo getPdfImageInfo(MultipartFile file) throws IOException {
        if (file.isEmpty()) {
            throw new IllegalArgumentException("上传的文件不能为空");
        }
        
        if (!isPdfFile(file)) {
            throw new IllegalArgumentException("只支持PDF格式文件");
        }
        
        try (InputStream inputStream = file.getInputStream();
             PDDocument document = PDDocument.load(inputStream)) {
            
            int pageCount = document.getNumberOfPages();
            long fileSize = file.getSize();
            String fileName = file.getOriginalFilename();
            
            return new PdfImageInfo(fileName, pageCount, fileSize, DEFAULT_DPI);
        }
    }
    
    /**
     * 将PDF文档转换为图片并打包成ZIP
     * 
     * @param document PDF文档对象
     * @param originalFilename 原始文件名
     * @return ZIP文件字节数组
     * @throws IOException 转换异常
     */
    private byte[] convertDocumentToImageZip(PDDocument document, String originalFilename) throws IOException {
        PDFRenderer pdfRenderer = new PDFRenderer(document);
        int pageCount = document.getNumberOfPages();
        
        ByteArrayOutputStream zipOutputStream = new ByteArrayOutputStream();
        
        try (ZipOutputStream zipOut = new ZipOutputStream(zipOutputStream)) {
            
            String baseFileName = extractBaseFileName(originalFilename);
            
            for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) {
                // 渲染PDF页面为图片
                BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, DEFAULT_DPI, ImageType.RGB);
                
                // 生成图片文件名
                String imageFileName = String.format("%s_page_%03d.%s", 
                    baseFileName, pageIndex + 1, IMAGE_FORMAT.toLowerCase());
                
                // 创建ZIP条目
                ZipEntry zipEntry = new ZipEntry(imageFileName);
                zipOut.putNextEntry(zipEntry);
                
                // 将图片写入ZIP
                ByteArrayOutputStream imageStream = new ByteArrayOutputStream();
                ImageIO.write(image, IMAGE_FORMAT, imageStream);
                zipOut.write(imageStream.toByteArray());
                zipOut.closeEntry();
                
                // 释放图片内存
                image.flush();
            }
        }
        
        return zipOutputStream.toByteArray();
    }
    
    /**
     * 将PDF单页转换为图片（用于预览）
     * 
     * @param file 上传的PDF文件
     * @param pageIndex 页面索引（从0开始）
     * @return 图片字节数组
     * @throws IOException 转换异常
     */
    public byte[] convertSinglePageToImage(MultipartFile file, int pageIndex) throws IOException {
        if (file.isEmpty()) {
            throw new IllegalArgumentException("上传的文件不能为空");
        }
        
        if (!isPdfFile(file)) {
            throw new IllegalArgumentException("只支持PDF格式文件");
        }
        
        try (InputStream inputStream = file.getInputStream();
             PDDocument document = PDDocument.load(inputStream)) {
            
            if (pageIndex < 0 || pageIndex >= document.getNumberOfPages()) {
                throw new IllegalArgumentException("页面索引超出范围: " + pageIndex);
            }
            
            PDFRenderer pdfRenderer = new PDFRenderer(document);
            BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, DEFAULT_DPI, ImageType.RGB);
            
            ByteArrayOutputStream imageStream = new ByteArrayOutputStream();
            ImageIO.write(image, IMAGE_FORMAT, imageStream);
            image.flush();
            
            return imageStream.toByteArray();
        }
    }
    
    /**
     * 从文件名提取基础名称（去除扩展名）
     * 
     * @param filename 原始文件名
     * @return 基础文件名
     */
    private String extractBaseFileName(String filename) {
        if (filename == null || filename.trim().isEmpty()) {
            return "converted_pdf";
        }
        
        // 移除PDF扩展名
        String baseName = filename.replaceAll("\\.[pP][dD][fF]$", "");
        
        // 处理特殊字符，确保文件名安全
        baseName = baseName.replaceAll("[^a-zA-Z0-9\u4e00-\u9fff\\-_]", "_");
        baseName = baseName.replaceAll("_{2,}", "_");
        baseName = baseName.replaceAll("^_|_$", "");
        
        if (baseName.isEmpty()) {
            baseName = "converted_pdf";
        }
        
        return baseName;
    }
    
    /**
     * 判断是否为PDF文件
     * 
     * @param file 上传的文件
     * @return 是否为PDF文件
     */
    private boolean isPdfFile(MultipartFile file) {
        String contentType = file.getContentType();
        String filename = file.getOriginalFilename();
        
        return ("application/pdf".equals(contentType)) || 
               (filename != null && filename.toLowerCase().endsWith(".pdf"));
    }
    
    /**
     * PDF图片信息类
     * 用于返回PDF文件的基本信息
     */
    public static class PdfImageInfo {
        private String fileName;
        private int pageCount;
        private long fileSize;
        private float dpi;
        private String estimatedImageSize;
        
        public PdfImageInfo(String fileName, int pageCount, long fileSize, float dpi) {
            this.fileName = fileName;
            this.pageCount = pageCount;
            this.fileSize = fileSize;
            this.dpi = dpi;
            this.estimatedImageSize = estimateImageSize(pageCount);
        }
        
        /**
         * 估算生成的图片包大小
         */
        private String estimateImageSize(int pages) {
            // 粗略估算：每页大约1-2MB（300 DPI PNG格式）
            long estimatedBytes = pages * 1500 * 1024; // 1.5MB per page
            
            if (estimatedBytes < 1024 * 1024) {
                return String.format("%.1f KB", estimatedBytes / 1024.0);
            } else {
                return String.format("%.1f MB", estimatedBytes / (1024.0 * 1024.0));
            }
        }
        
        // Getters
        public String getFileName() { return fileName; }
        public int getPageCount() { return pageCount; }
        public long getFileSize() { return fileSize; }
        public float getDpi() { return dpi; }
        public String getEstimatedImageSize() { return estimatedImageSize; }
        
        // Setters
        public void setFileName(String fileName) { this.fileName = fileName; }
        public void setPageCount(int pageCount) { this.pageCount = pageCount; }
        public void setFileSize(long fileSize) { this.fileSize = fileSize; }
        public void setDpi(float dpi) { this.dpi = dpi; }
        public void setEstimatedImageSize(String estimatedImageSize) { this.estimatedImageSize = estimatedImageSize; }
    }
}